package com.semeureka.fault.mina;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.filter.codec.demux.MessageEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.semeureka.fault.bean.Device;
import com.semeureka.fault.bean.Device.Phase;
import com.semeureka.fault.bean.Group;
import com.semeureka.fault.bean.Rawdata;
import com.semeureka.fault.service.DeviceService;
import com.semeureka.fault.util.Message;
import com.semeureka.fault.util.Threshold;
import com.semeureka.frame.util.HexUtils;

@Component
public class RawdataEncoder implements MessageEncoder<Rawdata> {
	private static final Logger logger = LoggerFactory.getLogger(RawdataEncoder.class);
	private static final AtomicInteger pid = new AtomicInteger();
	@Autowired
	private DeviceService deviceService;

	@Override
	public void encode(IoSession session, Rawdata rawdata, ProtocolEncoderOutput out) throws Exception {
		int protocolType = rawdata.getGroup().getProtocolType();
		IoBuffer buffer = IoBuffer.allocate(255); // 协议最大长度
		if (rawdata.getContent() != null) {
			buffer.put(rawdata.getContent());
		} else if (protocolType == 2) { // GPRS协议
			encodeGprs(session, rawdata, buffer);
		} else if (protocolType == 3) { // 101协议
			encodeP101(session, rawdata, buffer);
		}
		if (buffer.flip().hasRemaining()) {
			byte[] content = new byte[buffer.remaining()];
			buffer.get(content);
			rawdata.setContent(content);
			rawdata.setCreateTime(new Date());
			out.write(buffer.flip());
		} else {
			logger.warn("无法编码的数据：{}", rawdata);
		}
	}

	private void encodeGprs(IoSession session, Rawdata rawdata, IoBuffer buffer) {
		int rawdataType = rawdata.getType();
		if (rawdataType == 0x83) { // 定时、自检应答
			fillGprsHead(rawdata.getGroup(), 0x83, buffer);
			fillGprsTail(buffer);
		} else if (rawdataType == Message.RT_RECALL) { // 召唤
			fillGprsHead(rawdata.getGroup(), 0x81, buffer);
			fillGprsTail(buffer);
		} else if (rawdataType == Message.RT_ADJUST) { // 遥调
			fillGprsHead(rawdata.getGroup(), 0x82, buffer);
			for (Message message : rawdata.getMessages()) {
				Threshold threshold = (Threshold) message;
				Device device = deviceService.findByGroupPhase(threshold.getGroup(), threshold.getPhase());
				buffer.put(device.getCode()).putUnsigned(0xE8).putUnsignedShort(threshold.getValue());
			}
			fillGprsTail(buffer);
		}
	}

	private void fillGprsHead(Group group, int ctrl, IoBuffer buffer) {
		buffer.putUnsigned(0x68);
		buffer.put(group.getHostCode()); // 主机长编码
		buffer.putUnsigned(0x68);
		buffer.putUnsigned(ctrl); // 控制码
		buffer.putUnsignedShort(0x000C); // 长度
		buffer.putUnsignedShort(0x0009); // 设备类型
		Device device = deviceService.findByGroupPhase(group, Phase.M);
		buffer.put(device.getCode()); // 主机短编码
		buffer.putUnsignedShort(pid.incrementAndGet()); // PID
		buffer.putUnsignedInt(0x00010001); // 总包、包编号
		buffer.putUnsignedShort(0x0000); // 数据域
	}

	private void fillGprsTail(IoBuffer buffer) {
		int position = buffer.position();
		buffer.putUnsignedShort(9, position - 11); // 重置长度
		buffer.putUnsignedShort(position - 2, position > 23 ? 0x0001 : 0x0000);// 重置数据域
		buffer.putUnsigned(HexUtils.sumupCrc(buffer, 0, position)); // 校验
		buffer.putUnsigned(0x16);
	}

	private void encodeP101(IoSession session, Rawdata rawdata, IoBuffer buffer) {
		int rawdataType = rawdata.getType();
		if ((rawdataType & 0xFFFFFF00) == 0) { // rawdataType是控制码
			buffer.putUnsigned(0x10);
			buffer.putUnsigned(rawdataType);
			byte[] hostCode = Arrays.copyOfRange(rawdata.getGroup().getHostCode(), 0, 2);
			ArrayUtils.reverse(hostCode);
			buffer.put(hostCode);
			buffer.putUnsigned(HexUtils.sumupCrc(buffer, 1, 3));
			buffer.putUnsigned(0x16);
		} else if (rawdataType == 0x64010600) { // 总召开始
			fillP101Head(rawdata.getGroup(), 0x73, 0x64010600, buffer);
			buffer.putUnsignedShort(0x0000);
			buffer.putUnsigned(0x14);
			fillP101Tail(buffer);
		} else if (rawdataType == 0x67010600) { // 对时开始
			fillP101Head(rawdata.getGroup(), 0x73, 0x67010600, buffer);
			buffer.putUnsignedShort(0x0000);
			fillP101Date(new Date(), buffer);
			fillP101Tail(buffer);
		}
	}

	private void fillP101Head(Group group, int ctrl, int type, IoBuffer buffer) {
		buffer.putUnsigned(0x68);
		buffer.putUnsignedShort(0x0C0C); // 长度
		buffer.putUnsigned(0x68);
		buffer.putUnsigned(ctrl); // 控制码
		byte[] hostCode = Arrays.copyOfRange(group.getHostCode(), 0, 2);
		ArrayUtils.reverse(hostCode);
		buffer.put(hostCode); // 链路
		buffer.putUnsignedInt(type); // 类型码
		buffer.put(hostCode); // 公共地址
	}

	private void fillP101Date(Date date, IoBuffer buffer) {
		Calendar cal = DateUtils.toCalendar(date);
		int ms = cal.get(Calendar.MILLISECOND) + cal.get(Calendar.SECOND) * 1000;
		buffer.putUnsigned(ms & 0xFF);
		buffer.putUnsigned(ms >> 8);
		buffer.putUnsigned(cal.get(Calendar.MINUTE));
		buffer.putUnsigned(cal.get(Calendar.HOUR_OF_DAY));
		buffer.putUnsigned(cal.get(Calendar.DAY_OF_MONTH));
		buffer.putUnsigned(cal.get(Calendar.MONTH) + 1);
		buffer.putUnsigned(cal.get(Calendar.YEAR) % 100);
	}

	private void fillP101Tail(IoBuffer buffer) {
		int len = buffer.position() - 4;
		buffer.putUnsigned(1, len); // 重置长度
		buffer.putUnsigned(2, len);
		buffer.putUnsigned(HexUtils.sumupCrc(buffer, 4, len)); // 校验
		buffer.putUnsigned(0x16);
	}
}
